using System;

namespace RapidHDL
{
	/// <summary>
	/// Summary description for Register.
	/// </summary>
	public class RegisterComponent : Component 
	{
		// clock
		// latch input
		// width
		// data connections

		public NodeVector InputNodes;
		public NodeVector OutputNodes;
		public NodeVector EnableNodes;
        public NodeVector ClockNodes;

		public ClockComponent ClockIn;

        public bool RisingEdge;

		int iInputWidth;

		public RegisterComponent(Component poParentComponent, string psName, ClockComponent poClockIn, int piInputWidth, bool pbRisingEdge, bool pbEnable) 
			: base (poParentComponent,psName)
		{
            RegisteredComponent = true;
            RisingEdge = pbRisingEdge;
            iInputWidth = piInputWidth;
			InputNodes = this.CreateNodeVector("REG_I",iInputWidth,NodeFlowType.Sink);
			OutputNodes = this.CreateNodeVector("REG_O",iInputWidth,NodeFlowType.Source);
            ClockNodes = this.CreateNodeVector("CLK_I", 1, NodeFlowType.Sink);

            ClockIn = poClockIn;

            ClockNodes.Connection = ClockIn.ClockOut;

            if (pbEnable)
            {
                EnableNodes = this.CreateNodeVector("ENABLE_I", 1, NodeFlowType.Sink);
            }            		
		}

        public override void GenerateStructure()
        {
            ClockIn.LinkRegister(this);	
        }

		public override void CalculateOutput()
		{
            // shouldn't be called for a register
		}

        public virtual void ClockEdgeEvent(bool pblnRisingEdge)
        {
            Node nEnableNode;
            
            if (pblnRisingEdge == RisingEdge)
            {
                SimulationDestabilizeComponent();

                if (EnableNodes != null)
                {
                    nEnableNode = EnableNodes[0];
                    if (nEnableNode.NodeState == NodeState.High)
                        OutputNodes.AssignNodeStates(InputNodes);
                    if (nEnableNode.NodeState == NodeState.Undefined)
                        OutputNodes.NodeVectorAsString = (new string('x', OutputNodes.Nodes.Count));
                }
                else 
                    OutputNodes.AssignNodeStates(InputNodes);

                SimulationStabilizeComponent();
            }

        }

        public override bool InitializeSimulation()
        {
            SimulationDestabilizeComponent();
            SimulationStabilizeComponent();
            return true;
        }

        public override bool TransformStructureToVerilog()
        {
            string sVerilog = "";
            //string sOutputVector = "";
            //string sOutput = "";

            ComponentVerilog.VerilogTraceLog("Starting To Write Register");

            foreach (NodeVector oNodeVector in this.OutputNodeVectors)
            {
                if (oNodeVector.Width == 1)
                    ComponentVerilog.WriteVerilogText("reg " + OutputNodes.Name + ";", 3);
                else
                {
                    int iStart = oNodeVector.Width - 1;
                    ComponentVerilog.WriteVerilogText("reg [" + iStart.ToString() + ":0] " + OutputNodes.Name + ";", 3);
                }
            }
            ComponentVerilog.WriteVerilogSkip();


            sVerilog = "always @(";
            if (RisingEdge)
                sVerilog += "posedge";
            else
                sVerilog += "negedge";
            sVerilog += " " + ClockNodes.Name + ")";
            ComponentVerilog.WriteVerilogText(sVerilog);
            ComponentVerilog.WriteVerilogText("begin");

            if (EnableNodes != null)
            {
                ComponentVerilog.WriteVerilogText("if (" + EnableNodes.Name + ")", 2);
                ComponentVerilog.WriteVerilogText("begin", 2);
            }            
            ComponentVerilog.WriteVerilogText(OutputNodes.Name + " <= " + InputNodes.Name + ";", 3);
            if (EnableNodes != null)
            {
                ComponentVerilog.WriteVerilogText("end", 2);
            }

            ComponentVerilog.WriteVerilogText("end");
            return true;
        }

	}
}
